05. 压制 commit

L3 - 压制介绍

要将 commit 压制在一起,我们要使用非常强大的 git rebase 命令。这是我 最爱的 命令之一,但我的确用了 很久 才掌握它。起初,我很难搞清楚它的工作原理,然后(在网上读了无数警告之后),我开始害怕实际使用它,因为担心它会不可逆转地破坏我的项目的 Git 历史。

但是现在我想告诉你, git rebase 实际并没有那么难,你可以勇敢地对你的仓库进行更改,而无需担心造成任何损害!(<-- 听起来相当鼓舞人心,对不对?!)我们首先概述一下压制的工作原理,然后将实际使用 git rebase 命令进行一些压制。

L3 - 压制理论

我使用的命令是:

$ git rebase -i HEAD~3

rebase 命令

git rebase 命令会将 commit 移动到一个 新基底(base) 上。在命令 git rebase -i HEAD~3 中,我们告诉 Git 使用 HEAD~3 作为其他所有 commit ( HEAD~2 HEAD~1 HEAD )将连接到的基底。

命令中的 -i 代表"交互式"。你 可以 在非交互模式下执行 rebase。在你学习如何 rebase 时,我明确建议你进行 交互式 rebase。

祖先引用

简短回顾一下, HEAD 表示你当前的位置(它可以指向多个东西,但通常会指向分支名称或直接指向一个 commit 的 SHA)。这里的 ~3 指"三个之前",所以 HEAD~3 将是你当前所在的 commit 向前三个的 commit。我们将在 git rebase 命令中使用对 commit的这种相对引用。

我来演示一下如何使用此命令将三个目标 commit 组合成一个。

L3 - 压制操作

在该命令中:

$ git rebase -i HEAD~3

HEAD~3 是即将被变基的 commit 的新基底的祖先引用。

以下哪项可以作为基底引用?

SOLUTION:
  • SHA
  • 分支名称
  • 标签名称

强制推送

在此视频中,我必要要强行推送分支。我不得不这样做,因为 GitHub 试图阻止我意外删除 commit。因为我使用了 git rebase 命令,实际上 清除 三个分别记录我添加了佛罗里达、巴黎和苏格兰的 commit。我使用 git rebase 将这些 commit 合并或 压制 为单个 commit。

使用 git rebase 会创建一个具有新 SHA 的新 commit。当我尝试使用 git push 将此 commit 推送至 GitHub 时,GitHub 知道接受此推送会删除那三个单独 commit,所以它拒绝了。因此,我不得不使用 git push -f 强制推送 这些 commit。

⚠️ 强行推送 ⚠️

在这个例子中,强行推送我的 commit 非常必要。如果你尝试推送 commit 而 GitHub 拒绝了它们,这实际上是对你的一种帮助,确保你已经审查过正在推送的 commit GitHub 上的 commit,以验证你不会意外覆盖你在远程仓库上存储的内容!

rebase 命令

来看看你可以使用 git rebase 执行哪些不同的命令:

  • 使用 p pick – 使 commit 保持原样
  • 使用 r reword – 保留 commit 的内容,但修改 commit 说明
  • 使用 e edit – 保留 commit 的内容,但先不要执行 commit,以便:
    • 添加新内容或文件
    • 删除内容或文件
    • 修改即将 commit 的内容
  • 使用 s squash – 将此 commit 的更改结合到之前的 commit 中(列表中位于其上面的 commit )
  • 使用 f fixup – 将此 commit 的更改结合到前一个 commit 中,但删除提交说明
  • 使用 x exec – 运行 shell 命令
  • 使用 d drop – 删除 commit

何时变基

可以看到, git rebase 命令非常强大。它可以帮助你编辑提交说明、重新排序 commit、合并 commit 等,真的是一款非常强大的工具。现在的问题是"应该 何时 进行变基?"。

每当你对 commit 进行变基,Git 将 为每个 commit 创建一个新的 SHA!这有很大的影响。对于 Git,SHA 为 commit 的标识符,因此不同的标识符代表着不同的 commit, 无论内容是否发生了变化。

如果你已推送了你想进行变基的 commit,则不应变基。如果你在与其他开发者协作,那么他们可能已经在使用你推送的 commit。如果你随后使用 git rebase 来进行更改,并强行推送 commit,则其他开发者现在将无法与远程仓库同步。他们需要对自己的 Git 仓库进行一些复杂的手术,使它们的仓库回到工作状态……甚至可能连这一点都做不了;他们可能得抛弃之前的所有工作,使用你新变基过且强制推送的 commit 重新开始。

小结

git rebase 命令可以用来做很多事情。

# 交互式 rebase
$ git rebase -i <base>

# 交互式地将 commit 变基到我们当前所在的 commit 向前三个的 commit
$ git rebase -i HEAD~3

在 commit 的交互式列表中,所有 commit 都以 pick 开头,但你可以使用其他命令( reword edit squash fixup exec drop )进行变换。

我建议你在变基 之前 创建一个备份(backup)分支,这样便能很容易返回到之前的状态。如果你对变基的结果满意,则可以删除 backup 分支!

进一步研究